package com.buer.train.api;

import com.buer.train.business.domain.DeptDO;
import com.buer.train.business.domain.MenuDO;
import com.buer.train.business.domain.RoleDO;
import com.buer.train.business.domain.UserDO;
import com.buer.train.business.service.DeptService;
import com.buer.train.business.service.MenuService;
import com.buer.train.business.service.RoleService;
import com.buer.train.business.service.UserService;
import com.buer.train.common.annotation.Auth;
import com.buer.train.common.annotation.Login;
import com.buer.train.common.constant.Constant;
import com.buer.train.common.eumn.RequestType;
import com.buer.train.common.eumn.ResultEnum;
import com.buer.train.common.handle.BaseException;
import com.buer.train.common.holder.LoginUser;
import com.buer.train.common.holder.LoginUserHolder;
import com.buer.train.common.holder.RequestTypeHolder;
import com.buer.train.common.utils.*;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/system")
@Api(tags = {"系统登录接口"})
public class LoginController {

    @Autowired
    private UserService userService;

    @Autowired
    private MenuService menuService;

    @Autowired
    private DeptService deptService;

    @Autowired
    private RoleService roleService;

    private static final Logger logger = LoggerFactory.getLogger(LoginController.class);

    @RequestMapping(value = "/login", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @ApiOperation("系统登录接口")
    @Login
    public BaseResult login(@RequestBody Map<String, Object> param) {
        Map<String, Object> resultMap = new HashMap<>();
        LoginUser loginUser = new LoginUser();
        String account = String.valueOf(param.get("account"));
        String passWord = String.valueOf(param.get("passWord"));
        String requestType = RequestTypeHolder.getRequestTypeHolder();
        loginUser.setRequestType(requestType);
        //参数验证
        checkParamLegality(account, passWord);
        //验证用户是否存在
        Map<String, Object> ifMap = new HashMap<>();
        ifMap.put("username",account);
        UserDO userDO = userService.getOne(ifMap);
        checkUserExist(userDO);
        //密码验证
        checkCorrectPassWord(userDO, passWord);
        //检查账号的判断参数，并设置到loginUser中
        checkBusinessParams(userDO);
        //获取用户对应的角色权限   //todo 2020年3月16日11:11:42 判断具体端登录类型 是否需要角色权限需要维护
        if (checkLoginType(requestType)) {
            setResource(userDO, loginUser, resultMap);
        }
        //实例化loginUser（对应不同用户类型）
        setParamByUserType(userDO, loginUser);
        //生成token
        String token = generateToken(account);
        resultMap.put("token", token);
        resultMap.put("roleSign", loginUser.getRoleSign());
        resultMap.put("deptId", loginUser.getDeptId());
        loginUser.setToken(token);
        //放入redis
        saveToRedis(loginUser, token, requestType,userDO);


        return BaseResult.build(ResultEnum.SUCCESS, resultMap);
    }

    @RequestMapping(value = "/getMenuList", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    @ApiOperation("获取菜单")
    @Auth
    public BaseResult getMenuList(){
        LoginUser loginUserHolder = LoginUserHolder.getLoginUserHolder();
        List<MenuDO> allResource = menuService.getAllResourceByUserId(loginUserHolder.getUserId());
        List<MenuDO> resultResource = new ArrayList<>();

        if (CheckEmptyUtil.isNotEmpty(allResource)) {
            //递归获取结果集
            getResource(allResource, resultResource);
        }

        return BaseResult.build(ResultEnum.SUCCESS, resultResource);
    }

    private void saveToRedis(LoginUser loginUser, String token, String requestType,UserDO userDO) {
        try {
//            区别类型
            String key = requestType + ":token:" + loginUser.getUserName();
            RedisUtil.set(key, loginUser, Constant.REDIS_EXPIRE);
//            同一数据
            String userKey = Constant.LOGINUSER + ":" + loginUser.getUserId();
            RedisUtil.set(userKey, userDO);
        } catch (Exception e) {
            logger.error("redis连接异常");
            throw new BaseException(ResultEnum.REDIS_SERVER_ERROR);
        }


    }

    private String generateToken(String account) {
        String token = Aes.aesEncrypt(account+":"+System.currentTimeMillis());
        return token;
    }

    private void setParamByUserType(UserDO userDO, LoginUser loginUser) {
        loginUser.setUserId(userDO.getUserId());
        loginUser.setName(userDO.getName());
        loginUser.setUserName(userDO.getUsername());
        loginUser.setDeptId(userDO.getDeptId());

        int currNodeDeptId=getCurrNodeDeptId(userDO.getDeptId());
        loginUser.setNodeDeptId(currNodeDeptId);

        Set<Integer> nodeDeptChilds=new HashSet<>();
        DeptDO deptDO = deptService.get(userDO.getDeptId());
        nodeDeptChilds.add(deptDO.getDeptId());
        getCurrNodeDeptChilds(userDO.getDeptId(),nodeDeptChilds);
        loginUser.setNodeDepts(nodeDeptChilds);

        RoleDO roleByUserId = roleService.getRoleByUserId(userDO.getUserId());
        loginUser.setRoleSign(roleByUserId.getRoleSign());
    }

    private void getCurrNodeDeptChilds(Integer deptId,Set<Integer> nodeDeptChilds) {
        Map<String,Object> map=new HashMap<>();
        map.put("parentId",deptId);
        map.put("type","0");
        List<DeptDO> list = deptService.list(map);
        for (DeptDO deptDO:list){
            nodeDeptChilds.add(deptDO.getDeptId());
            getCurrNodeDeptChilds(deptDO.getDeptId(),nodeDeptChilds);
        }
    }

    private void getCurrDeptChilds(Integer deptId,Set<Integer> deptChilds) {
        deptChilds.add(deptId);
        Map<String,Object> map=new HashMap<>();
        map.put("parentId",deptId);
        List<DeptDO> list = deptService.list(map);
        for (DeptDO deptDO:list){
            getCurrDeptChilds(deptDO.getDeptId(),deptChilds);
        }
    }

    private int getCurrNodeDeptId(Integer deptId) {
        DeptDO deptDO = deptService.get(deptId);
        if(deptDO.getType()==0){
            return deptDO.getDeptId();
        }else {
            if(deptDO.getParentId()!=0){
                return getCurrNodeDeptId(deptDO.getParentId());
            }else {
                return 0;
            }
        }
    }


    private void setResource(UserDO userDO, LoginUser loginUser, Map<String, Object> resultMap) {
        List<MenuDO> allResource = menuService.getAllResourceByUserId(userDO.getUserId());
        List<MenuDO> resultResource = new ArrayList<>();

        if (CheckEmptyUtil.isNotEmpty(allResource)) {
            //递归获取结果集
            getResource(allResource, resultResource);

            resultMap.put("resource", resultResource);

            List<String> resources = allResource.stream()
                    .filter(resource -> CheckEmptyUtil.isNotEmpty(resource.getPerms()))
                    .map(resource -> resource.getPerms())
                    .collect(Collectors.toList());

            loginUser.setResources(resources);
        }
    }

    private void getResource(List<MenuDO> allResource, List<MenuDO> resultResource) {
        for (MenuDO resourceDO : allResource) {
            if (Objects.equals(resourceDO.getType(), 0)
                    && Objects.equals(resourceDO.getParentId(),0)) {
                resultResource.add(resourceDO);
            }
        }

        for (MenuDO resourceDO : resultResource) {
            doGetResource(resourceDO, allResource);
        }
    }

    private void doGetResource(MenuDO resourceDO, List<MenuDO> allResource) {
        for (MenuDO resource : allResource) {
            if (Objects.equals(resourceDO.getMenuId(), resource.getParentId())) {
                if (Objects.equals(resource.getType(), 2)) {
                    resourceDO.getBtnShow().add(resource);
                } else {
                    resourceDO.getChildren().add(resource);
                    doGetResource(resource, allResource);
                }
            }
        }
    }

    /**
     * @return boolean  true 校验权限， false 不作校验
     */
    private boolean checkLoginType(String appTypeStr) {
        if (RequestType.HT.toString().equals(appTypeStr)) {
            return true;
        } else {
            return false;
        }
    }

    private void checkBusinessParams(UserDO userDO) {
        if(Objects.equals(userDO.getStatus(),0)){
            logger.error("用户账户被禁用");
            throw new BaseException(ResultEnum.USER_FORBIDDEN);
        }
    }


    public void checkParamLegality(String account, String passWord) {
        if(CheckEmptyUtil.isEmpty(account)){
            throw new BaseException(ResultEnum.USERNAME_NULL);
        }

        if(CheckEmptyUtil.isEmpty(passWord)){
            throw new BaseException(ResultEnum.PASSWORD_NULL);
        }
    }

    private void checkCorrectPassWord(UserDO userDO, String passWord) {
        String reqPassWord = MD5Utils.encrypt(userDO.getUsername(), passWord);
        if (!Objects.equals(reqPassWord, userDO.getPassword())) {
            logger.error("输入的密码有误");
            throw new BaseException(ResultEnum.ERROR_PASSWORD);
        }
    }


    public void checkUserExist(UserDO userDO) {
        if (userDO == null) {
            logger.error("用户不存在");
            throw new BaseException(ResultEnum.NO_EXIT_USER);
        }
    }

}
